﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;

namespace Soul.Expressions
{
    internal class LambdaParser
    {
        public static List<StringToken> GetStringTokens(string input)
        {
            // 正则表达式用于匹配字符串常量
            string pattern = @"@?""(?<data>(?:[^""\\]|\\.)*?)""";

            // 使用正则表达式进行匹配
            var result = new List<StringToken>();
            var matches = Regex.Matches(input, pattern);

            // 提取匹配的字符串常量
            foreach (Match match in matches)
            {
                result.Add(new StringToken(match.Value, match.Groups["data"].Value));
            }

            return result;
        }

        public static List<CharToken> GetCharTokens(string input)
        {
            // 正则表达式用于匹配字符串常量
            string pattern = @"'(?<data>[^\\']|\\[abfnrt'""\?\\0xu]{1})'";

            // 使用正则表达式进行匹配
            var result = new List<CharToken>();
            var matches = Regex.Matches(input, pattern);

            // 提取匹配的字符串常量
            foreach (Match match in matches)
            {
                result.Add(new CharToken(match.Value, match.Groups["data"].Value[0]));
            }

            return result;
        }

        public static bool TryConstant(string token, out Expression expression)
        {
            if (LambdaConstant.TryNULL(token, out object nullValue))
            {
                expression = Expression.Constant(nullValue);
                return true;
            }
            if (LambdaConstant.TryBoolean(token, out bool boolValue))
            {
                expression = Expression.Constant(boolValue);
                return true;
            }
            if (LambdaConstant.TryString(token, out string stringValue))
            {
                expression = Expression.Constant(stringValue);
                return true;
            }
            if (LambdaConstant.TryInt64(token, out long longValue))
            {
                expression = Expression.Constant(longValue);
                return true;
            }
            if (LambdaConstant.TryInt32(token, out int intValue))
            {
                expression = Expression.Constant(intValue);
                return true;
            }
            if (LambdaConstant.TryDouble(token, out double doubleValue))
            {
                expression = Expression.Constant(doubleValue);
                return true;
            }
            if (LambdaConstant.TryFloat(token, out float floatValue))
            {
                expression = Expression.Constant(floatValue);
                return true;
            }
            if (LambdaConstant.TryDecimal(token, out decimal decimalValue))
            {
                expression = Expression.Constant(decimalValue);
                return true;
            }
            expression = null;
            return false;
        }

        public static bool TryNot(string token, out string newToken)
        {
            var match = Regex.Match(token, @"^!(?<expr>.+)");
            if (match.Success)
            {
                newToken = match.Groups["expr"].Value;
                return true;
            }
            newToken = string.Empty;
            return false;
        }

        public static bool TryBinary(string token, out BinaryToken binaryToken)
        {
            var types = new List<string>
            {
                 @"\*|/|%",
                 @"\+|\-",
                 @">=|<=|>|<",
                 @"==|!=",
                 @"&&",
                 @"\|\|"
            };
            foreach (var item in types)
            {
                var pattern = $@"\(?(?<left>[^\+\-\(\)\s]+)\s*(?<type>{item})\s*(?<right>[^\+\-\(\)\s]+)\)?";
                var match = Regex.Match(token, pattern);
                if (match.Success)
                {
                    binaryToken = new BinaryToken(match.Value, match.Groups["left"].Value, match.Groups["type"].Value, match.Groups["right"].Value);
                    return true;
                }
            }
            binaryToken = null;
            return false;
        }

        public static bool TryCall(string token, out MethodCallToken methodCallToken)
        {
            var match = Regex.Match(token, @"(?<name>\b\w+)\s*\((?<args>[^\(\)]*)\)");
            if (match.Success)
            {
                var name = match.Groups["name"].Value;
                var args = match.Groups["args"].Value.Split(',').Select(s => s.Trim()).ToArray();
                methodCallToken = new MethodCallToken(match.Value, name, args);
                return true;
            }
            methodCallToken = null;
            return false;
        }

        public static bool TryUnary(string token, out UnaryToken unaryToken)
        {
            var match = Regex.Match(token, @"\((?<data>.+)\)");
            if (match.Success)
            {
                var data = match.Groups["data"].Value;
                unaryToken = new UnaryToken(match.Value, data);
                return true;
            }
            unaryToken = null;
            return false;
        }

        public static bool TryMember(string token, out MemberAccessToken memberToken)
        {
            var match = Regex.Match(token, @"(?<own>[a-zA-Z_][a-zA-Z0-9_]*).(?<prop>[a-zA-Z_][a-zA-Z0-9_])");
            if (match.Success)
            {
                var own = match.Groups["own"].Value;
                var prop = match.Groups["prop"].Value;
                memberToken = new MemberAccessToken(match.Value, own, prop);
                return true;
            }
            memberToken = null;
            return false;
        }
    }

    internal class UnaryToken
    {
        public string Token { get; set; }
        public string Expr { get; set; }

        public UnaryToken(string token, string expr)
        {
            Token = token;
            Expr = expr;
        }
    }

    internal class MemberAccessToken
    {
        public string Token { get; set; }
        public string Own { get; set; }
        public string Prop { get; set; }

        public MemberAccessToken(string token, string own, string prop)
        {
            Token = token;
            Own = own;
            Prop = prop;
        }
    }

    internal class BinaryToken
    {
        public string Token { get; set; }
        public string Left { get; set; }
        public string Type { get; set; }
        public string Right { get; set; }

        public BinaryToken(string token, string left, string type, string right)
        {
            Token = token;
            Left = left;
            Type = type;
            Right = right;
        }

        public ExpressionType GetExpressionType()
        {
            switch (Type)
            {
                case "==":
                    return ExpressionType.Equal;
                case "!=":
                    return ExpressionType.NotEqual;
                case ">":
                    return ExpressionType.GreaterThan;
                case "<":
                    return ExpressionType.LessThan;
                case ">=":
                    return ExpressionType.GreaterThanOrEqual;
                case "<=":
                    return ExpressionType.LessThanOrEqual;
                case "+":
                    return ExpressionType.Add;
                case "-":
                    return ExpressionType.Subtract;
                case "*":
                    return ExpressionType.Multiply;
                case "/":
                    return ExpressionType.Divide;
                case "%":
                    return ExpressionType.Modulo;
                case "&&":
                    return ExpressionType.AndAlso;
                case "||":
                    return ExpressionType.OrElse;
                case "!":
                    return ExpressionType.Not;
            }
            throw new NotImplementedException(Type);
        }
    }

    internal class MethodCallToken
    {
        public string Token { get; set; }
        public string Name { get; set; }
        public string[] Arguments { get; set; }

        public MethodCallToken(string token, string name, string[] arguments)
        {
            Token = token;
            Name = name;
            Arguments = arguments;
        }
    }

    internal class StringToken
    {
        public string Token { get; set; }
        public string Value { get; set; }

        public StringToken(string token, string value)
        {
            Token = token;
            Value = value;
        }
    }

    internal class CharToken
    {
        public string Token { get; set; }
        public char Value { get; set; }

        public CharToken(string token, char value)
        {
            Token = token;
            Value = value;
        }
    }

}
